(function(){

    if (typeof self === 'undefined' || !self.Prism || !self.document || !document.querySelector) {
        return;
    }
    
    function $$(expr, con) {
        return Array.prototype.slice.call((con || document).querySelectorAll(expr));
    }
    
    function hasClass(element, className) {
      className = " " + className + " ";
      return (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(className) > -1
    }
    
    // Some browsers round the line-height, others don't.
    // We need to test for it to position the elements properly.
    var isLineHeightRounded = (function() {
        var res;
        return function() {
            if(typeof res === 'undefined') {
                var d = document.createElement('div');
                d.style.fontSize = '13px';
                d.style.lineHeight = '1.5';
                d.style.padding = 0;
                d.style.border = 0;
                d.innerHTML = '&nbsp;<br />&nbsp;';
                document.body.appendChild(d);
                // Browsers that round the line-height should have offsetHeight === 38
                // The others should have 39.
                res = d.offsetHeight === 38;
                document.body.removeChild(d);
            }
            return res;
        }
    }());
    
    function highlightLines(pre, lines, classes) {
        var ranges = lines.replace(/\s+/g, '').split(','),
            offset = +pre.getAttribute('data-line-offset') || 0;
    
        var parseMethod = isLineHeightRounded() ? parseInt : parseFloat;
        var lineHeight = parseMethod(getComputedStyle(pre).lineHeight);
    
        for (var i=0, range; range = ranges[i++];) {
            range = range.split('-');
                        
            var start = +range[0],
                end = +range[1] || start;
            
            var line = document.createElement('div');
            
            line.textContent = Array(end - start + 2).join(' \n');
            line.setAttribute('aria-hidden', 'true');
            line.className = (classes || '') + ' line-highlight';
    
            //if the line-numbers plugin is enabled, then there is no reason for this plugin to display the line numbers
            if(!hasClass(pre, 'line-numbers')) {
                line.setAttribute('data-start', start);
    
                if(end > start) {
                    line.setAttribute('data-end', end);
                }
            }
    
            line.style.top = (start - offset - 1) * lineHeight + 'px';
    
            //allow this to play nicely with the line-numbers plugin
            if(hasClass(pre, 'line-numbers')) {
                //need to attack to pre as when line-numbers is enabled, the code tag is relatively which screws up the positioning
                pre.appendChild(line);
            } else {
                (pre.querySelector('code') || pre).appendChild(line);
            }
        }
    }
    
    function applyHash() {
        var hash = location.hash.slice(1);
        
        // Remove pre-existing temporary lines
        $$('.temporary.line-highlight').forEach(function (line) {
            line.parentNode.removeChild(line);
        });
        
        var range = (hash.match(/\.([\d,-]+)$/) || [,''])[1];
        
        if (!range || document.getElementById(hash)) {
            return;
        }
        
        var id = hash.slice(0, hash.lastIndexOf('.')),
            pre = document.getElementById(id);
    
        if (!pre) {
            return;
        }
        
        if (!pre.hasAttribute('data-line')) {
            pre.setAttribute('data-line', '');
        }
    
        highlightLines(pre, range, 'temporary ');
    
        document.querySelector('.temporary.line-highlight').scrollIntoView();
    }
    
    var fakeTimer = 0; // Hack to limit the number of times applyHash() runs
    
    Prism.hooks.add('complete', function(env) {
        var pre = env.element.parentNode;
        var lines = pre && pre.getAttribute('data-line');
        
        if (!pre || !lines || !/pre/i.test(pre.nodeName)) {
            return;
        }
        
        clearTimeout(fakeTimer);
        
        $$('.line-highlight', pre).forEach(function (line) {
            line.parentNode.removeChild(line);
        });
        
        highlightLines(pre, lines);
        
        fakeTimer = setTimeout(applyHash, 1);
    });
    
    if(window.addEventListener) {
        window.addEventListener('hashchange', applyHash);
    }
    
    })();